
import 'dart:io';
import 'package:ble2025_plugin/ble2203_plugin.dart';
import 'package:ble2025_plugin_example/util/OtaSendData.dart';
import 'package:ble2025_plugin_example/util/app_all_value.dart';
import 'package:ble2025_plugin_example/util/event_bus.dart';
import 'package:ble2025_plugin_example/util/http_api.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:rtk_ota_plugin/rtk_ota_plugin.dart';
import 'package:rtk_ota_plugin/rtk_status.dart';

//固件升级页面
class OtaPage extends StatefulWidget {
  String? mac;
  String? name;
  OtaPage({this.mac,this.name});

  @override
  State<OtaPage> createState() => _OtaPageState();
}

class _OtaPageState extends State<OtaPage> {

  String fileNetWorkPath = "https://mp-1d374e45-404a-4b05-b029-40992bd928d2.cdn.bspapp.com/J2203_03_V000_8_20230613.zip";
  String filePath = "未下载".tr;
  late String savePath;
  double _downloadRatio = 0.0;
  String _downloadIndicator = '0.00%';
  late CancelToken _token;
  bool _downloading = false;
  TextEditingController filePathController = TextEditingController();
  String framePath = "未选择".tr;
  String uiPath = "未选择".tr;
  String updateFirmwareTip = "";
  String updateUiTip = "";
  AppAllValueController controller = Get.find();
  //是否处于更新设备中
  bool isUpdate = false;
  //是否处于UI更新设备中
  bool isUIUpdate = false;
  //更新文件是否下载完成
  bool isDownFile = false;
  int packageIndex = 0;//块编号
  int Count = 0;//包编号
  int sendTotalPackageLength = 0;//块发送长度
  int sendTotalBlockLength = 0;//总发送长度
  bool isFinish = false;//本块数据发送是否完成
  bool isSendFinish = false;//所有数据发送是否完成
  Uint8List sendData = Uint8List.fromList([]);//发送的数据块
  int sendDataCrc = 0;//本块数据的crc
  int maxLength = 244;//手机一次能发送的最大数据长度
  Map hashMap = {};
  Uint8List checkByteValue = Uint8List.fromList([]);
  Uint8List endByteValue = Uint8List.fromList([]);
  List<int> binByte = [];

  @override
  void initState() {
    getSavePath();
    super.initState();
    filePathController.text = fileNetWorkPath;
    //直接返回字节
    EventBus().on('dataCallBackAsList', (arg){
      List<int> value = arg;
      if(value.isNotEmpty){
        switch(value[0]){
          case 0x41:
            controller.writeData(checkByteValue);
            break;
          case 0x92:
            if (value[1] == 1) {//查询是否需要升级
              if (value[2] == 1) {//不需要升级
                print('不需要升级');
              } else if (value[2] == 0) {//需要升级
                print('需要升级');
                startUpdate();
              } else if (value[2] == 2) {//固件没有升级
                print('需要先升级固件文件');
              }
            } else if (value[1] == 2) {//升级数据发送完成
              print('UI升级完成');
              if (value[2] == 0) {//失败
                print('升级失败');
              } else if (value[2] == 1) {//成功
                print('升级成功');
              }
            } else if (value[1] == 3) {
              print('crc检测');
            } else if (value[1] == 0x10) {//超时
              print('超时');
            } else if (value[1] == 0x70) {
              controller.writeData(checkByteValue);
            }
            break;
          case 0x93:
            if (value[1] == 1) {
              sendData = nextPackage();
              //进度
              double percent = packageIndex / hashMap.length;
              print('${'进度'.tr}${percent.toStringAsFixed(2)}');
              setState(() {
                updateUiTip = '${'进度'.tr}${percent.toStringAsFixed(2)}';
              });
              if (sendData.isEmpty) {
                isUIUpdate = false;
                endUpdateFile();
                setState(() {
                  updateUiTip = '升级完成'.tr;
                });
              } else {
                startUpdate();
              }
            } else if (value[1] == 0) {
              print('重发${packageIndex}');
              isFinish = false;
              sendData = Uint8List.fromList(hashMap[packageIndex]);
              if (sendData.isEmpty) return;
              sendDataCrc = OtaSendData.CalcCRC16(sendData, sendData.length);
              sendTotalPackageLength = sendTotalPackageLength - sendData.length;
              startUpdate();
            }
            break;
        }
      }
    });

    RtkOtaPlugin().otaListener((type, progress){
      switch(type){
        case RtkStatus.STATUS_DEVICE_CONNECTING:
          setState(() {
            updateFirmwareTip = "设备连接中".tr;
          });
          break;
        case RtkStatus.STATUS_READY_FINISH:
          setState(() {
            updateFirmwareTip = "准备完成".tr;
          });
          break;
        case RtkStatus.STATUS_OTA_FINISH:
          setState(() {
            updateFirmwareTip = "OTA环境准备结束".tr;
          });
          break;
        case RtkStatus.STATUS_OTA_READYING:
          setState(() {
            updateFirmwareTip = "正在准备OTA环境".tr;
          });
          break;
        case RtkStatus.STATUS_UPGRADING:
          setState(() {
            updateFirmwareTip = "${'进度'.tr}:${progress}%";
          });
          break;
        case RtkStatus.STATUS_UPGRAD_SUCC:
          setState(() {
            updateFirmwareTip = "更新完成".tr;
          });
          isUpdate = false;
          break;
      }
    });
  }

  ///获取保存的路径
  void getSavePath(){
    getTemporaryDirectory()
        .then((tempDir) => {savePath = tempDir.path + 'update.zip'});
  }

  ///下载文件
  void _downloadFile() {
    if (_downloading == true) {
      return;
    }
    _token = CancelToken();
    _downloading = true;
    HttpApi().doDownload(filePathController.text, savePath, cancelToken: _token,
        progress: (int received, int total) {
          // 下载进度
          setState(() {
            _downloadRatio = (received / total);
            if (_downloadRatio == 1) {
              _downloading = false;
            }
            _downloadIndicator = (_downloadRatio * 100).toStringAsFixed(2) + '%';
            filePath = "${'下载进度'.tr}: $_downloadIndicator";
            print("${'下载进度'.tr}: $_downloadIndicator");
          });
        }, completion: () {
          // 下载成功
          _downloading = false;
          isDownFile = true;
          print("下载完成:");
          setState(() {
            filePath = savePath;
          });
          RtkOtaPlugin().setFilePath(savePath);
          // FlutterLoadingHud.showToast(message: "\"下载完成\"");
        }, failure: (error) {
          // 下载出错
          _downloading = false;
          setState(() {
            filePath = "下载出错".tr;
          });
          print("下载出错:${error.toString()}");
          // FlutterLoadingHud.showToast(message: error.message);
        });
  }
  ///取消下载
  void _cancelDownload() {
    if (_downloadRatio < 1.0) {
      _token.cancel();
      _downloading = false;
      setState(() {
        _downloadRatio = 0;
        _downloadIndicator = '0.00%';
      });
    }
  }
  ///删除文件
  void _deleteFile() {
    try {
      File downloadedFile = File(savePath);
      if (downloadedFile.existsSync()) {
        downloadedFile.delete();
      } else {
        print("文件不存在");
      }
    } catch (e) {
      print("删除文件异常");
    }
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("固件升级".tr),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: [
          buildFilePathWidget(),
          buildDownFile(),
          buildUpdateFirmwareFile(),
          buildUiFile(),
          buildUpdateUiFile(),

        ],
      ),
    );
  }

  Widget buildFilePathWidget(){
    return Container(
      margin: EdgeInsets.only(left: 10,right: 10),
      height: 100,
      child: Row(
        children: [
          Text('${'升级文件地址'.tr}：'),
          Expanded(child: TextField(
            controller: filePathController,
            decoration: InputDecoration(
                hintText: '请输入升级文件路径'.tr
            ),
            textAlign: TextAlign.center,
            maxLines: null,
            keyboardType: TextInputType.text,
            textInputAction: TextInputAction.done,
          ))
        ],
      ),
    );
  }

  Widget buildDownFile(){
    return Container(
      margin: const EdgeInsets.only(left: 10,right: 10),
      height: 80,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: [
          Container(
            width: 100,
            margin: const EdgeInsets.only(right: 10),
            child: ElevatedButton(
              child: Text('下载文件'.tr),
              onPressed: () async {
                if(filePathController.text.isNotEmpty){
                  _downloadFile();
                }
              },
            ),
          ),
          Expanded(child: Text(filePath))
        ],
      ),
    );
  }

  Widget buildUiFile(){
    return Container(
      margin: const EdgeInsets.only(left: 10,right: 10),
      height: 80,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: [
          Container(
            width: 100,
            margin: const EdgeInsets.only(right: 10),
            child: ElevatedButton(
              child: Text('资源文件'.tr),
              onPressed: () async {
                uiPath = await RtkOtaPlugin().getColorPath();
                setState(() {
                });
                controller.writeData(BleSDK.enterOTA());
              },
            ),
          ),
          Expanded(child: Text(uiPath))
        ],
      ),
    );
  }

  Widget buildUpdateUiFile(){
    return Container(
      margin: const EdgeInsets.only(left: 10,right: 10),
      height: 40,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: [
          Container(
            width: 200,
            margin: const EdgeInsets.only(right: 10),
            child: ElevatedButton(
              child: Text('UI文件升级'.tr),
              onPressed: () async {
                if(isDownFile){
                  if(isUIUpdate){
                    controller.showContentDialog("正在更新中，请勿重复点击".tr);
                  }else{
                    uiUpdate(uiPath);
                  }
                }else{
                  controller.showContentDialog("更新文件未下载".tr);
                }
              },
            ),
          ),
          Expanded(child: Text(updateUiTip))
        ],
      ),
    );
  }

  Widget buildUpdateFirmwareFile(){
    return Container(
      margin: const EdgeInsets.only(left: 10,right: 10),
      height: 40,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: [
          Container(
            width: 200,
            margin: const EdgeInsets.only(right: 10),
            child: ElevatedButton(
              child: Text('固件升级'.tr),
              onPressed: () async {
                if(isDownFile){
                  if(isUpdate){
                    controller.showContentDialog("正在更新中，请勿重复点击".tr);
                  }else{
                    update(savePath);
                  }
                }else{
                  controller.showContentDialog("更新文件未下载".tr);
                }
              },
            ),
          ),
          Expanded(child: Text(updateFirmwareTip))
        ],
      ),
    );
  }

  ///开始升级
  void update(String path){
    isUpdate = true;
    controller.disConnectDevice();
    if(Platform.isAndroid){
      Future.delayed(Duration(seconds: 1),(){
        RtkOtaPlugin().initDfuAdapter(widget.mac!,path);
      });
    }else{
      Future.delayed(Duration(seconds: 1),(){
        RtkOtaPlugin().initIosDfuAdapter(widget.name!,path);
      });

    }
  }


  ///资源文件升级
  void uiUpdate(String path)async{
    //校验文件
    isUIUpdate = true;
    setState(() {
      updateUiTip = '准备更新环境'.tr;
    });

    File file = File(path);
    Uint8List binFile = await file.readAsBytes();
    List<int> fileValue = binFile.toList();
    int fileNumber = fileValue[1];
    binByte = OtaSendData.generateValue(fileValue.length-16);
    List<int> fileMd5Value = OtaSendData.generateValue(16);
    BleSDK.arrayCopy(fileValue, 0, binByte, 0, binByte.length);
    BleSDK.arrayCopy(fileValue, fileValue.length-16, fileMd5Value, 0, 16);
    String contentMd5 = await RtkOtaPlugin().md5(path);
    String lastMd5 = ResolveUtil.unit8List2String(fileMd5Value);
    contentMd5 = contentMd5.split(',')[1];
    // print('contetnMd5:${contentMd5}');
    // print('lastMd5:${lastMd5}');
    if(contentMd5.toLowerCase() == lastMd5.toLowerCase()){
      checkByteValue = OtaSendData.checkUpdateFile(binByte,fileMd5Value,fileNumber);
      endByteValue = OtaSendData.endUpdateFile(binByte,fileMd5Value,fileNumber);
      //分块
      int length = (binByte.length % 4096 == 0) ? binByte.length ~/ 4096 : binByte.length ~/ 4096 + 1;
      hashMap = {};
      for (int i = 0; i < length; i++) {//数据分块，一块4096长度
        int valueLength = 4096;
        if (i * 4096 + valueLength > binByte.length) {
          valueLength = binByte.length - i * 4096;
        }
        List<int> value = OtaSendData.generateValue(valueLength);
        BleSDK.arrayCopy(binByte, i * 4096, value, 0, valueLength);
        hashMap.addAll({i:value});
      }
      sendData = Uint8List.fromList(hashMap[packageIndex]);
      sendDataCrc = OtaSendData.CalcCRC16(sendData, sendData.length);
      //循环发送
      controller.writeData(BleSDK.GetDeviceTime());
    }else{
      //文件校验失败
      isUIUpdate = false;
      controller.showContentDialog("文件校验失败".tr);
    }
  }

  void endUpdateFile() {//数据全部发送完成检查是否升级成功
    controller.writeData(endByteValue);
  }


   Uint8List nextPackage() {
    isFinish = false;
    packageIndex++;
    List<int> sendData = hashMap[packageIndex]??OtaSendData.generateValue(0);
    if (sendData.isNotEmpty) sendDataCrc = OtaSendData.CalcCRC16(sendData, sendData.length);
    return Uint8List.fromList(sendData);
  }

  void startUpdate() {
    if (maxLength == 0 || isFinish) return;
    if (sendData.isEmpty) {
      endUpdateFile();
      return;
    }
    int dataLength = sendData.length;
    int contentLength = isFinish ? maxLength - 8 : maxLength - 6;
    isFinish = false;
    if (sendTotalBlockLength + contentLength >= dataLength) {//块数据发送完毕
      contentLength = dataLength - sendTotalBlockLength;
      if (contentLength + 8 >maxLength) {
        contentLength = maxLength - 6;
        sendTotalBlockLength += contentLength;
      } else {
        sendTotalBlockLength = 0;
        isFinish = true;
      }
    } else {
      sendTotalBlockLength += contentLength;
    }
    if (sendTotalPackageLength + contentLength >= binByte.length) {//发送完毕
      contentLength = binByte.length - sendTotalPackageLength;
      isSendFinish = true;
    }
    int length = isFinish ? contentLength + 8 : contentLength + 6;
    List<int> value = OtaSendData.generateValue(length);
    value[0] = getCmd(1);
    value[1] = (packageIndex & 0xff);
    value[2] = (packageIndex >> 8 & 255);
    value[3] = Count++;
    BleSDK.arrayCopy(binByte, sendTotalPackageLength, value, 4, contentLength);
    if (isFinish) {
      value[value.length - 4] = ((sendDataCrc) & 0xff);
      value[value.length - 3] = ((sendDataCrc >> 8) & 0xff);
    }
    int crc16 = OtaSendData.CalcCRC16(value, value.length - 2);
    value[value.length - 2] = ((crc16) & 0xff);
    value[value.length - 1] = ((crc16 >> 8) & 0xff);
    sendTotalPackageLength += contentLength;
    controller.offerValue(value);
    if (isFinish) {
      Count = 0;
      controller.offerValue(getEnd(packageIndex, sendDataCrc));
      controller.offerData();
    } else {
      startUpdate();
    }
  }


  Uint8List getEnd(int count, int crc) {
    List<int> binMd5 = md5Crc(hashMap[count]);
    List<int> value = OtaSendData.generateValue(24);
    value[0] = getCmd(1);
    value[1] = (count & 0xff);
    value[2] = (count >> 8 & 255);
    value[3] = 0xff;
    value[4] = ((crc) & 0xff);
    value[5] =  ((crc >> 8) & 0xff);
    BleSDK.arrayCopy(binMd5, 0, value, 6, binMd5.length);
    int crc16 = OtaSendData.CalcCRC16(value, value.length - 2);
    value[value.length - 2] = ((crc16) & 0xff);
    value[value.length - 1] = ((crc16 >> 8) & 0xff);
    return Uint8List.fromList(value);
  }


  List<int> md5Crc(List<int> data){
    int c = 0;
    int TempCRC = 0xFFFF;
    for (int j = 0; j<data.length-2 ; j++) {
      int temp = data[j];
      c = temp & 0x00FF;
      TempCRC^=c;
      for (int m=0; m<8; m++){
        int v = TempCRC & 0x0001;
        if (v == 1){
          TempCRC >>= 1;
          TempCRC ^= 0xA001;
        }
        else
        {
          TempCRC >>= 1;
        }
      }
    }

    List<int> value = OtaSendData.generateValue(2);
    value[0] = TempCRC%256;
    value[1] = TempCRC~/256;
    return value;
  }




  int getCmd(int dataType) {
    int cmd = 0;
    if (dataType == 0) {
      cmd = 0x92;
    } else {
      cmd = 0x93;
    }
    return cmd;
  }

  @override
  void dispose() {
    super.dispose();
    EventBus().off('dataCallBackAsList');
  }


}
